/*
 * DensityView.java
 *
 * Created on Mar 8, 2012
 *
 * Copyright (c) 2012 Artur Rataj
 *
 * This code is distributed under the terms of the GNU Library
 * General Public License, either version 3 of the license or, at
 * your option, any later version.
 */

package pl.gliwice.iitis.spatialnn.tools;

import java.io.*;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;

import pl.gliwice.iitis.graphutils.Point;
import pl.gliwice.iitis.spatialnn.network.MembraneType;
import pl.gliwice.iitis.spatialnn.network.NeuronType;
import pl.gliwice.iitis.spatialnn.network.construct.ConstructionParams;
import pl.gliwice.iitis.spatialnn.network.construct.Gaussian;

/**
 * Visualises density of clouds of randomly drawn points.
 * 
 * @author Artur Rataj
 */
public class DensityView {
    /**
     * Width of the output image in pixels.
     */
    protected int width;
    /**
     * Height of the output image in pixels.
     */
    protected int height;
    /**
     * Physical resolution of the output image.
     */
    protected int dpi;
    /**
     * Direction of the neutral angle.
     */
    double na;
    /**
     * Output image.
     */
    public BufferedImage out;
    /**
     * Graphics to draw in the output image.
     */
    protected Graphics2D g2;
    /**
     * Ascent of the font.
     */
    protected int ascent;
    /**
     * Number of samples per cloud.
     */
    int samples;
    
    /**
     * Creates a new instance of <code>DensityView</code>.
     * 
     * @param width width of the output image in pixels
     * @param height height of the output image in pixels
     * @param dpi physical resolution of the image
     * @param na direction of the neutral angle
     */
    public DensityView(int width, int height, int dpi, double na, int samples) {
        this.width = width;
        this.height = height;
        this.dpi = dpi;
        this.na = na;
        out = new BufferedImage(width, height,
                BufferedImage.TYPE_BYTE_GRAY);
        g2 = (Graphics2D)out.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
        g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_PURE);
        g2.setColor(Color.WHITE);
        g2.fillRect(0, 0, this.width, this.height);
        g2.setFont(new Font("dialog", Font.PLAIN, (int)Math.round(0.2*dpi)));
        ascent = g2.getFontMetrics().getAscent();
        this.samples = samples;
    }
    /**
     * Plots a cloud.
     * 
     * @param origin origin of the cloud
     * @param dist distribution of distance to the origin
     * @param angle distribution of directed angle to <code>na</code>
     * @param label label of the cloud
     * @param sampleRatio multiplies <code>samples</code> to get the actual number
     * of samples to use
     * @param drawOrigin if the origin should be marked with a small circle
     */
    public void plot(Point origin, Gaussian dist, Gaussian angle,
            String label, double sampleRatio, boolean drawOrigin) {
        WritableRaster r = out.getRaster();
        double scale = 12*dpi;
        for(int i = 0; i < samples; ++i) {
            double d = Math.max(0, dist.draw());
            double a = na + angle.draw();
            int x = (int)Math.round(origin.x + Math.cos(a)*d*scale);
            int y = (int)Math.round(origin.y + Math.sin(a)*d*scale);
            if(x < 0 || x >= r.getWidth())
                continue;
            if(y < 0 || y >= r.getHeight())
                continue;
            int s = r.getSample(x, y, 0) - 1;
            if(s < 0)
                s = 0;
            r.setSample(x, y, 0, s);
        }
        double radius = 0.03*dpi;
        g2.setColor(Color.BLACK);
        if(drawOrigin)
            g2.draw(new Ellipse2D.Double(origin.x - radius + 0.5, origin.y - radius + 0.5,
                    2*radius, 2*radius));
        g2.drawString(label, (float)(origin.x + radius*3),
                (float)(origin.y + ascent/2 + radius*4));
    }
    public static void main(String[] args) {
        final int S = 1;
        final int W = 800*S;
        final int H = 100*S;
        final int SAMPLES = 700000;
        DensityView dv = new DensityView(W, H, 90*S, 0, SAMPLES);
        ConstructionParams membrane = MembraneType.OUT.getConstructionParams();
        ConstructionParams symmetric = NeuronType.scanWidthCP(
                new Gaussian(2, 0), true);
        ConstructionParams scanning = NeuronType.scanHeightCP(
                new Gaussian(1, 0), 1.0);
        dv.plot(new Point(W*0.05, H/2), membrane.inDist,
                new Gaussian(0, membrane.inAngleSigma), "", 1.0, true);
        dv.na = -Math.PI/4.0;
        dv.plot(new Point(W*0.3, H/2), symmetric.inDist,
                new Gaussian(0, symmetric.inAngleSigma), "", 0.5, true);
        dv.na = Math.PI/4.0;
        dv.plot(new Point(W*0.3, H/2), symmetric.inDist,
                new Gaussian(0, symmetric.inAngleSigma), "", 0.5, false);
        dv.na = 0;
        dv.plot(new Point(W*0.55, H/2), scanning.inDist,
                new Gaussian(0, scanning.inAngleSigma), "", 1.0, true);
        try {
            ImageIO.write(dv.out, "PNG", new File("dv.png"));
        } catch (IOException e) {
            throw new RuntimeException("could not save image: " + e.toString());
        }
    }
}
